﻿\subsection{虚拟内存管理}

 虚拟内存的引入消除了物理内存分布上的不连续性，各个应用程序可访问达到 \GiB{4} 的虚拟地址空间。
页目录寄存器 \lstinline{CR3} 的变换，使得进程的虚拟地址空间彼此独立。
通过将物理页框映射到不同进程的虚拟地址空间，还可使进程共享同样的物理页框，各个用户进程共享相同的操作系统内核空间就是通过这种方式做到的。


\newcommand\vaddr[3]{\lstinline{(#1,} \lstinline{#2,} \lstinline{#3)}}
 
\subsubsection{虚拟页面预分配策略}

为进程分配虚拟页面资源时，常采用预分配策略。
当进程中的某个线程请求分配虚拟内存空间时，只标记该虚拟内存空间对应的页表项，而暂时不分配物理页框，物理页框的分配推迟到线程访问该虚拟内存空间触发缺页故障时进行。
IA-32 体系结构下的页表项在 \lstinline{PTE.PRESENT} 清空时，剩余所有的位均被忽略；在 \lstinline{PTE.PRESENT} 置位时第 9 \TO 11 位被 MMU 忽略。
这些忽略的位可供操作系统使用，为了避免与其他在 \lstinline{PTE.PRESENT} 置位时有定义的属性位冲突，本操作系统只使用 9 \TO 11 位。
当某线程请求虚拟页面资源时，将页表项中的第 9 位（记作 \lstinline {PTE.ALLOCATED}）置位。
表示该页表项已经被占用，后续分配虚拟页面不应考虑此页表项。


假定虚拟地址由三元组 \vaddr{PDE_INDEX}{PTE_INDEX}{OFFSET} 构成，页目录中第 \lstinline{SELF} 项的高 20 为存放页目录自身的物理地址，以实现分页体系的自洽。
若需要分配连续的 \lstinline{N} 个虚拟页面，则需要遍历页表，查找满足 \lstinline{PTE.PRESENT} 和 \lstinline{PTE.ALLOCATED} 均清空的页表项。
各页表在物理内存中离散存放，但是在虚拟地址上是连续的。
例如，当 \lstinline{SELF} 取 \lstinline{1023} 时，页表的范围为 \vaddr{1023}{0}{0} \TO \vaddr{1023}{1023}{1023} （包含页目录自身），即 \lstinline{0xFFC0'0000} \TO \lstinline{0xFFFF'FFFF}。
遍历页表项时，设置 \lstinline{nBegin} 和 \lstinline{nEnd} 两个标志标记可分配的连续页表项的起始和结束位置，当二者的间隔 \lstinline{nEnd - nBegin} 达到 \lstinline{N} 时，查找成功，将这些页表项中的 \lstinline{PTE.ALLOCATED} 属性位置位，虚拟页面的分配即完成。


\subsubsection{将物理页框映射到虚拟页面}

将物理页框映射到虚拟页面的过程本质上就是处理缺页故障的过程。
分页机制下，所有的内存地址都通过 MMU 中的地址变换机构转换成对物理地址的访问。
通过前述物理页框分配方案获取到可用的物理页框后，还需要在页表中记录物理页框号及若干属性位。
除了一般的缺页故障，还有一类特殊的缺页故障——页表缺失。
不论是一般缺页还是页表缺页，缺页故障发生的虚拟地址都被记录在 \lstinline{CR2} 寄存器中，仅从表面上无法确定故障是一般缺页还是页表缺页。


对于由三元组表示的虚拟地址。
假定缺页类型是一般缺页，则需要填补页表项 \vaddr{SELF}{PDE_INDEX}{PTE_INDEX * 4}；按照一般缺页的假设访问页表项时，如果还存在页表缺页，则还会再触发一次缺页故障，在页目录中填补缺失页表的页目录项 \vaddr{SELF}{SELF} {PDE_INDEX * 4} 后可正常处理一般缺页。
从上述讨论中可发现，页表缺页的特征是缺页地址高 10 位为页目录用于自映射的 PDE 索引号，据此可分情况对一般缺页和页表缺页进行相应处理。

